using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;
using Microsoft.Vsa;
using System.Diagnostics;
using System.Reflection;
using System.Text;


using BO = PHSRAG.Insight21.BusinessObjects;
using Utility = PHSRAG.Utility;
using ICachePolicy = PHSRAG.Insight21.Policy.ICachePolicy;

namespace PHSRAG.Insight21.ScriptEngine
{
	/// <summary>
	/// ScriptEngineHost implements IVsaSite interface, which allows running of 
	/// VB scripts within an application.
	/// </summary>
	public class ScriptEngineHost : IVsaSite, IDisposable
	{
		#region Private members
		private IVsaEngine		vsaEngine;
		private StringBuilder	compileErrorList;
		private System.Collections.Hashtable  intrinsicList;
        private bool disposed;
        #endregion

		#region Constants
		protected string systemDll		= "system.dll";
		protected string mscorlibDll	= "mscorlib.dll";
		protected string scriptObject	= "Script";
		#endregion


		/// <summary>
		/// Constructor for the ScriptEngine Host
		/// </summary>
		/// <param name="rootMoniker">name which uniquely identifies the script if multiple are running at once.</param>
		/// <param name="rootNamespace">root namespace used by the script engine</param>
		/// <param name="generateDebug">tells the script engine whether to generate debugging information or not.</param>
		public ScriptEngineHost( string rootMoniker, string rootNamespace, bool generateDebug )
		{
			compileErrorList = new StringBuilder();

			// initialize the hashtable so it's ready to use.
			intrinsicList = new Hashtable();
            
			vsaEngine = new Microsoft.VisualBasic.Vsa.VsaEngine();
			vsaEngine.RootMoniker = rootMoniker;  
			vsaEngine.Site = this;
			vsaEngine.InitNew();
			vsaEngine.RootNamespace = rootNamespace; 

			vsaEngine.Reset();
			vsaEngine.RevokeCache();

			// Turn on debugging
			vsaEngine.GenerateDebugInfo = generateDebug;

			
			// Always load core assemblies
			IVsaItems    items = vsaEngine.Items;
			IVsaReferenceItem refItem = (IVsaReferenceItem)items.CreateItem(systemDll, VsaItemType.Reference, VsaItemFlag.None);
			refItem.AssemblyName = systemDll;
			
			// mscorlib.dll
			refItem = (IVsaReferenceItem)items.CreateItem(mscorlibDll, VsaItemType.Reference, VsaItemFlag.None);
			refItem.AssemblyName = mscorlibDll;

			// this
			string  assemName = Assembly.GetExecutingAssembly().Location;
			refItem = (IVsaReferenceItem)items.CreateItem(assemName,
				VsaItemType.Reference,
				VsaItemFlag.None);
			refItem.AssemblyName = assemName;
		}

		/// <summary>
		/// Pubilic method to add a DLL reference to the Script host engine
		/// </summary>
		/// <param name="dllName">the FULL path of the DLL to run.</param>
		public void AddDLLReference ( string vsaItemName )
		{
			IVsaReferenceItem refItem = (IVsaReferenceItem) vsaEngine.Items.CreateItem( vsaItemName,
				VsaItemType.Reference,
				VsaItemFlag.None);
			refItem.AssemblyName = vsaItemName;
		}

		/// <summary>
		/// Adds the VB script code to the engine for running
		/// </summary>
		/// <param name="scriptCode"></param>
		public void AddScriptReference ( string scriptCode )
		{
			IVsaCodeItem codeItem = (IVsaCodeItem)vsaEngine.Items.CreateItem(scriptObject,
				VsaItemType.Code,
				VsaItemFlag.None);
			codeItem.SourceText = scriptCode;
		}

		public void AddIntrinsicObject( string objectName, object intrinsicObject )
		{
			IVsaGlobalItem intrinsic = (IVsaGlobalItem)vsaEngine.Items.CreateItem ( 
															objectName, 
															VsaItemType.AppGlobal, 
															VsaItemFlag.None );
			//intrinsic.ExposeMembers = true;

			intrinsic.TypeString = "System.Object"; 
		
            // Add the object to the hashtable of intrinsics
			intrinsicList.Add( objectName, intrinsicObject );
		}


		/// <summary>
		/// Method wrapper to compile and run the script. Note that if the script object doesn't 
		/// already exist in the Items collection, it will be added in.
		/// </summary>
		/// <param name="theScript">ScriptEngine.Script object passed in to be run</param>
		/// <param name="assemblyType">entry point Assembly name to run</param>
		/// <param name="methodToRun">the method within the VB script to run</param>
		public void RunScript ( Script theScript, string assemblyType, string methodToRun )
		{
			
			// add the script if it doesn't exist
			if ( vsaEngine.Items[scriptObject] == null )
			{
				IVsaCodeItem codeItem = (IVsaCodeItem)vsaEngine.Items.CreateItem(scriptObject,
					VsaItemType.Code,
					VsaItemFlag.None);
				codeItem.SourceText = theScript.ScriptText;
                VsaPersistSite
			}
			vsaEngine.Run();

			// Call the VB entry point
			Type type = vsaEngine.Assembly.GetType( assemblyType );

			MethodInfo test = (type.GetMethod( methodToRun ));

			test.Invoke (null, null);
		}

		/// <summary>
		/// Compiles the passes in script's code
		/// </summary>
		/// <param name="theScript">ScriptEngine.SCript object whose code will be compiled.</param>
		/// <returns>true if it compiles, false if it doesn't. NOTE: On compile failure,
		/// OnCompileError is triggered.</returns>
		public bool Compile ( Script theScript )
		{
			//Dll references
			AddDLLReference("System.Web");

			IVsaCodeItem codeItem = (IVsaCodeItem)vsaEngine.Items.CreateItem(scriptObject,
				VsaItemType.Code,
				VsaItemFlag.None);
			codeItem.SourceText = theScript.ScriptText;

			if ( !vsaEngine.Compile())
				throw new Exception (compileErrorList.ToString ());				
			return true;
		}

		/// <summary>
		/// Method required by IVsaEngine interface. This method does
		/// nothing and returns Null.
		/// </summary>
		/// <param name="name">Name, which currently does nothing</param>
		/// <returns></returns>
		object IVsaSite.GetGlobalInstance( string name )
		{
			foreach(DictionaryEntry entry in intrinsicList)

				// carefully comparing the item names
				// to the name of the item requested by the engine
				if (string.Compare(entry.Key.ToString(), name, true) == 0)
					// and if we find it, return the object instance to the engine
					return entry.Value;

			// if we couldn't find any instance
			// for that name, return null.
			return null;

		}


		/// <summary>
		/// Method required by IVsaEngine interface. This method currently does 
		/// nothing, and sets the output Byte pe and Byte debugInfo to null.
		/// </summary>
		/// <param name="pe"></param>
		/// <param name="debugInfo"></param>
		void IVsaSite.GetCompiledState(out Byte[] pe, out Byte[] debugInfo)
		{
			pe = debugInfo = null;
		}


		/// <summary>
		/// Method required by IVsaEngine interface. This method does
		/// nothing and returns Null.
		/// </summary>
		/// <param name="itemName">some item name which isn't used.</param>
		/// <param name="eventSourceName">some item name which isn't used.</param>
		/// <returns></returns>
		object IVsaSite.GetEventSourceInstance(string itemName, string eventSourceName)
		{
			return null;
		}

		void IVsaSite.Notify(string notify, object info)
		{}

		/// <summary>
		/// Event fired when a compile error occurs. Note that the bool returned is whether or not to 
		/// continue returning erros or not
		/// </summary>
		/// <param name="e">the IVSaError encountered during compliation.</param>
		/// <returns></returns>
		bool IVsaSite.OnCompilerError(IVsaError e)
		{
			compileErrorList.AppendFormat( "Error of severity '{0}' on line: '{1}' '{2}' \r\n",  e.Severity, e.Line, e.Description );
			return true;
		}

		/// <summary>
		/// Cleans up the VSAEngine.
		/// </summary>
		void IDisposable.Dispose()
		{
         Dispose(true);

         GC.SuppressFinalize(this);
		}

      /// <summary>
      /// Desctructor
      /// </summary>
      ~ScriptEngineHost()
      {
         Dispose(false);
      }

      /// <summary>
      /// Dispose implementation
      /// </summary>
      /// <param name="disposeManagedResources">Determines if resources are freed</param>
      protected virtual void Dispose(bool disposeManagedResources)
      {
         if (! disposed)
         {
            if (disposeManagedResources)
            {
               if (vsaEngine != null)
               {
                  vsaEngine.Close();
               }
            }

            disposed = true;
         }
      }
	}
}
